﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;

namespace BrasilFramework.Comum.Silverlight.Utilitario
{
    public static class Objeto
    {

        public static string Serializar<T>(this T fonte, IEnumerable<Type> tiposConhecidos = null)
        {
            DataContractSerializer contrato = new DataContractSerializer(typeof(T), tiposConhecidos);
            MemoryStream memoria = new MemoryStream();
            contrato.WriteObject(memoria, fonte);
            memoria.Position = 0;

            return Convert.ToBase64String(memoria.ToArray());
        }

        public static T Desserializar<T>(string dados)
        {
            DataContractSerializer contrato = new DataContractSerializer(typeof(T));

            MemoryStream memoria = new MemoryStream(Convert.FromBase64String(dados));

            return (T)contrato.ReadObject(memoria);
        }

        /// <summary>
        /// Ler o valor de uma propriedade de um objeto
        /// </summary>
        /// <param name="objeto">Objeto</param>
        /// <param name="propriedade">Nome da propriedade</param>
        /// <returns>Valor da propriedade</returns>
        public static object LerValor(object objeto, string propriedade)
        {
            if (objeto == null)
                return null;

            int idx = propriedade.IndexOf('.');
            return idx > 0 ?
                LerValor(objeto.GetType().GetProperty(propriedade.Remove(idx)).GetValue(objeto, null), propriedade.Substring(idx + 1))
                : objeto.GetType().GetProperty(propriedade).GetValue(objeto, null);
        }

        /// <summary>
        /// Atribui um valor à uma propriedade em um objeto
        /// </summary>
        /// <param name="objeto">Objeto</param>
        /// <param name="propriedade">Nome da propriedade</param>
        /// <param name="valor">Valor a ser atribuído</param>
        public static void AtribuirValor(object objeto, string propriedade, object valor)
        {
            int idx = propriedade.IndexOf('.');
            if (idx > 0)
            {
                PropertyInfo property = objeto.GetType().GetProperty(propriedade.Remove(idx));
                if (property.GetValue(objeto, null) == null)
                    property.SetValue(objeto, Activator.CreateInstance(property.PropertyType), null);
                AtribuirValor(property.GetValue(objeto, null), propriedade.Substring(idx + 1), valor);
            }
            else
            {
                Type tipo = objeto.GetType().GetProperty(propriedade).PropertyType;
                if (tipo.IsGenericType && tipo.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                {
                    Type tipoPrimitivo = Nullable.GetUnderlyingType(objeto.GetType().GetProperty(propriedade).PropertyType);

                    if (valor == null)
                        objeto.GetType().GetProperty(propriedade).SetValue(objeto, null, null);
                    else if (tipoPrimitivo.IsEnum)
                        objeto.GetType().GetProperty(propriedade).SetValue(objeto, Enum.Parse(tipoPrimitivo, Convert.ToString(valor), false), null);
                    else
                        objeto.GetType().GetProperty(propriedade).SetValue(objeto, valor, null);
                }
                else
                    objeto.GetType().GetProperty(propriedade).SetValue(objeto, valor, null);
            }
        }
    }
}
